home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1994 #2
/
Monster Media No. 2 (Monster Media)(1994).ISO
/
prog_gen
/
gcoope10.zip
/
USER10.TXT
< prev
Wrap
Text File
|
1994-07-21
|
57KB
|
1,329 lines
GCOOPE Version 1.0
Generic C-language Object Oriented Programming Environment Version 1.0
by Brian L. Price
Released as Public Domain July, 1994.
Table of Contents
I. Introduction to Objects....................................1
A. Object-ively Defined....................................1
B. Genericity isn't just for canned goods..................2
C. C. Inheritance without genetics.........................3
D. Introducing GCOOPE......................................3
II. The GCOOPE Environment.....................................5
A. Object handles..........................................5
B. Class variables.........................................6
C. Instance Variables......................................7
D. Methods.................................................7
E. Function Dispatching....................................9
1. 1. g is the Name.....................................9
2. The Smalltalk Connection............................11
3. Local method caching................................11
4. Steering the instance...............................13
F. Classes and Pseudo Classes.............................14
G. Flag Classes...........................................15
III. Creating a class definition..............................16
A. Layout of a class definition module...................16
B. Method definition section.............................16
C. Installation routine..................................17
IV. Expansion and Growth......................................18
Appendices
A. Appendix A. The GCOOPE USER'S GROUP.......................19
B. Appendix B. About the Author in case you're really bored..20
i
GCOOPE Version 1.0 User's Guide
By Brian L. Price July, 1994
I. Introduction to Objects
A. Object-ively Defined
Before diving into GCOOPE, let us take a few
moments to examine the concept object. My pocket
dictionary provides a starting point with two
interesting definitions:
1. Object - Something serving as a focus of
attention or ACTION.
2. Object - (Grammer) A noun that receives or is
affected by the action of a verb....
An object is first of all a thing, something that
exists in a perceptible form, in other words data.
That data is the focus of actions, it is changed,
translated, or simply accessed by a verb (method).
If we consider the verb to be a method for
performing an action, and that method is described as a
computer algorithm, we have the 'active' portion of the
definition.
Another way of saying object is active data. Now
how do we apply this concept to computer programming?
We must first lay out some useful principles. Lets
borrow some tried and true programming principles as a
start:
1. Modularity - modular code is self contained
(at least as much as possible), and it has a
well defined interface to the outside world.
Since our object concept is already a
seperate entity from its surroundings,
enforcing the principle of modularity helps
to keep it that way.
2. Orthagonality - The set of methods that we
define to act upon our object's data should
be orthagonal. In other words if you define
an add operation, you must define a subtract
operation. This goes hand in hand with our
first principle. If our set of methods is
complete and orthagonal, then there is no
need for outside intervention.
So our concept of object now consists of a piece
of data that can be acted upon by an orthagonal set of
methods with a well defined interface, and all this is
contained as a seperate entity from its surroundings.
Lets call this encapsulation.
1
Now, by itself, our encapsulated object concept
isn't more powerful than procedurally written code.
(Although it would be easier to debug.) We need a few
more concepts to build upon what we already have.
B. Genericity isn't just for canned goods.
If you have ever been short on cash, or frugal,
you have probably purchased a generic product.
Something that is generic is the same as any other
thing of the same type or class, at least in all the
functional ways. A generic item can be substituted for
any other item of the same type.
What if we develop a set of encapsulated objects,
a subset of whose methods are remarkably similar in
function, lets say that those methods are math
functions. Now if one of those objects has integer
data and another has floating point data, an add
operation performs the same type of action on both
objects. Thus we could say that in this case add is a
generic action.
If we provide a suitable method for detecting
which object should be called depending upon the data
type, we can develop a generic function add. It won't
matter if its operand is of type float or integer
because the correct object method will perform the
actual operation. Thus we have the concept of a
generic function label.
This concept makes our object concept a bit more
powerful. Now, wherever we have similar operations
performed on different types of data, we can define one
label which we can then use for that operation no
matter what the actual data type is. We have
simplified our interface, since there are fewer
function labels required.
More importantly, we no longer have to know the
exact type or structure of the piece of data we are
manipulating, all we have to know is that the operation
we want to perform is a valid one for that object. To
simplify if the desired action is filling a basket with
fruit, do we really care if it gets filled with peaches
in place of tangerines? Genericity gives us a degree
of data independence.
2
C. C. Inheritance without genetics
We now have encapsulated objects with generic
methods, what could make this concept more powerful ?
In traditional programming we push complexity down the
procedure chain. The real work gets done in seperate
functions that we define to do a job. This allows us
to concentrate on the big picture without being
overwhelmed by details. The layout of a program looks
like an upside down tree structurally.
What if we have written an object to do a simple
task and now we want to do a more difficult task that
has the simple task as a necessary sub-task.
Traditionally, we would call a subroutine. We could do
that with our objects, but is there a better way ?
Consider the upside down structure tree of the
traditional program to be a family tree and the answer
is obvious. What if we could define a new object that
would automatically gain all the capabilities and data
structures of the old object ? This is the concept
called inheritance. If we define a child object with
the old object as its parent, we can gain all its
abilities (methods) through inheritance.
Can we do better ? Sure, we can allow multiple
ancestory ie. more than one parent, we can allow
partial ancestory ie. gaining only a subset of the
parent's capabilities, and we can redefine the parent
capabilities adding our own structure and actions on
top of what we inherit.
Inheritance lets us both change and build upon the
characteristics of the parent. This concept is made
even more powerful by our earlier insistance on
encapsulation. The parent can be changed to adapt it
to new requirements and as long as the original
interface and capabilities are not changed or removed,
it makes no difference to the child object.
D. Introducing GCOOPE
We have reached the point where we have developed
a quite powerful and useful concept. Now how do we
apply it ?
Brute force is always a good first shot, so we
could write a program module that would define a data
type or structure and methods (ie functions) to perform
actions upon it. Well, we'll probably need more than
one so we'll have to create and track each....
INSTANCE! Hmm, we have a set of these data regions or
instances, and we need only one set of methods to act
upon them since they are all members of the same,...
CLASS!
3
Okay so we have this set of methods and data
structure definitions for a class of object, lets call
this the class definition. We need some sort of
instance tracking database, lets use a variable to
represent each instance. Call it type object, so I
guess the variables would be OBJECT HANDLES.
There's the first step, now lets incorporate the
concept of genericity. We need some way of tracking
all these methods for each of the .... GENERIC (shape
changing) functions. Heck, we need some way of
tracking all these generic function labels. Routing
all these generic calls is going to be as bad as
running a taxi service in Manhattan, we need a ....
DISPATCHER to tell them where to go.
Now for inheritance, we'll just add our ancestor's
methods and instance memory to our own, of course we
need a way to add our own methods that will overwrite
any inherited methods for the same generic, and we have
to keep track of which instance of the parent class
belongs to our own instance,... you know we need a
couple of object classes just to do all these things.
At this point GCOOPE politely knocks at your door
offering its services. GCOOPE has lists to track
object classes and instances, it has a powerful
function dispatcher and instance tracking mechanism, in
addition it addresses problems we haven't even
considered yet in this discussion. Topics like default
destructors, temporary object deletion, error handling,
class definition objects, and more. Best of all GCOOPE
is written in and for the C programming language, bare
metal power cloaked in an object friendly environment.
4
II. The GCOOPE Environment
In this section, we will explore the strengths and
weaknesses of the GCOOPE system and its implementation.
GCOOPE is a powerful system but there are pitfalls and costs
in using it. GCOOPE, like most other real world systems, is
a product of choices between often conflicting goals and the
resulting system is defined by those design choices.
A. Object handles
The data type object, defined in both pcstruct.h
and GCOOPE31.h, is primarily used as a 'handle' for an
object class or instance. The type object is defined
in such a way that it is always at least 32 bits in
size. It is normally defined as a long integer.
For practical purposes, you can consider the
object handle to be the object although in actuality it
serves as a special type of index/pointer with internal
structure members. For the actual details of the
object handle structure, consult the T.R.M. (Technical
Reference Manual).
A secondary use of the data type object is to
return values from a method function. When used in
this manner, it should be cast as the appropriate data
type if it is being used to return something other than
an object handle. See the basic class libraries and
the gcoope10.h file for assistance macros.
5
B. Class variables
Class variables are variables or data structures
that are accessible to all instances of that class.
They are static variables that are the same for all
instances of the class.
The structure for a class variable area is defined
in the class definition, each class can have at most
one class variable structure/area. Another way of
stating this is to say that the class variable
structure area is a shared memory area private to class
members but shared among those same members. All class
variables must be accessed as members of the class
variable structure.
WARNING: In GCOOPE it is possible to access the class
variable area of another class. DON'T. This type
of practice defeats the purpose of encapsulation.
If a change occurs to the structure in the owning
class, you are in the same situation that you
would be in with C++ friends, or normal C code,
namely code re-write and re-compile time!
In order to access the class variable area use the
public kernel function, getCVptr. Full details of this
function is given in the T.R.M. The class variable
area is automatically created when the class definition
is installed.
When a class definition is installing itself as an
instance of class 'Class', the size of the class
variable structure must be passed as the cvSize
parameter.
6
C. Instance Variables
Instance variables are variables or data
structures that belong to an instance of a class. This
is a private static memory area for the instance. Each
instance of a class will have a separate instance
variable memory area.
The structure for an instance variable area is
defined in the class definition. Each class may have
at most one instance variable structure. All instance
variables must be referenced as members of that
structure.
WARNING: In GCOOPE, it is possible to access the
instance memory for another class, or ancestor
instance. DON'T. This practice makes your code
dependant on the internal structure of those other
modules, it defeats the entire purpose of
encapsulation. Using this type of practice will
result in code that is full of side effects and
cross dependancies.
To create the instance variable memory area, you
must use the public kernel function makeInst. Full
details of this function is given in the T.R.M. This
function should only be called within the constructor
(New) method of a class. It must be called prior to
any ancestor constructor calls.
To access the instance variable memory area, you
use the public kernel function getIVptr. Full details
on the use of this function is given in the T.R.M.
When a class definition is installing itself as an
instance of the class 'Class', the size of the instance
variable structure must be passed as the parameter
ivSize.
D. Methods
The generic GCOOPE method prototype is defined as:
typedef object (*method)(object,...);
A function of type method takes at least one
parameter of type object and returns a type object.
The first passed parameter must always be an object
handle of type object. Additional parameters may be
passed.
7
In our method type definition, you may observe the
use of the variable argument parameter ... This is
handy because we can use that one type method no matter
how many parameters are actually required by an actual
method. This does present some dangers.
First, passing a pointer as a variable argument
parameter is like running barefoot through a minefield.
Second, passing different data types in a variable
argument list is like adding machine gun and mortar
fire to that minefield. Unless your name is Superman,
you will take serious hits.
At first glance, it seems we've put ourselves in a
dilemma, we have a powerful abstraction that is too
dangerous to use, right? Wrong. Why are you passing
pointers to a method in the first place ??? You should
be passing object handles! Object handles are much
safer because they undergo some error checking and
their internal structural integrity is verifiable. In
fact, you can add even more error-checking on top of
the built-in checks in your class definitions.
As for multiple data types, we already have a type
object which can be used to pass almost any other data
type. Why not use it? If you only pass one parameter
in the variable argument field and if you are careful
in your design of object class definitions, it is
convient and fairly safe to pass a pointer or other
data type. However, with multiple parameters, take
heed.
1. RULE #1
When writing a method for use by an
applications program, if more than one parameter
is to be passed in the variable argument portion,
always use type object for all variable argument
list parameters, and always use an object handle
to an object instance rather than any pointers to
memory areas.
Another solution is to use the experimental
strong typing option. See typing.h. The best
approach is probably to use a combination of the
above advice, judicious use of strong typing and
when in doubt be sure and use the return value
typedefs.
8
E. Function Dispatching
The function dispatching and instance tracking
system in GCOOPE version 1.0 is far more sophisticated
than in the earlier. Used correctly, they can make
your programming chores easier, used incorrectly you
will need a good debugger.
1. 1. g is the Name
The function g is GCOOPE's function
dispatcher, its usage is deceptively simply. Even
though complete details are given in the Technical
Reference Manual for the GCOOPE kernel, I'll
repeat the usage info here.
returnObjectValue = g(generic)(objectInstance,
[methodParms,...]);
Where: returnObjectValue is a return value of
type object that is returned from the dispatched
method; generic is the generic function value of
type generic; objectInstance is the object type
object handle of the instance or class being acted
upon; and the optional methodParms,.... are the
parameters passed to the method.
If you want the details of its operation, see
the Technical Reference Manual, here, just take
for granted that it works as advertised.
WARNING: SEE RULE #1
1. Incorrect casting of the return value
either in the calling routine or in the
method executed. 32 bit values are
always passed, however depending on the
method, all 32 bits may not be valid.
Be sure to clearly specify in the class
definitions what values are being
returned by their methods. Also,
whenever possible all methods with the
same generic name should return the same
data type. Otherwise you will be using
the alternate definition of generic.
9
2. Passing the wrong type in the method
parameter list. GCOOPE is a weakly
typed system, method parameters are NOT
type checked, so stay alert. Wherever
possible in your method definitions be
sure and validate the recieved
parameters.
If possible, use the experimental
strong typing option. It DOES provide
type checking for method parameters,
although it is a bit awkward to use.
3. Passing a char or int type as the
objectInstance parameter instead of an
object type. Can you say "abnormal
program termination" ?
It cannot be stressed enough, GCOOPE is
weakly typed, this is both a blessing and a curse,
a strength and a weakness. If you are the type of
programmer that commonly expects the compiler to
catch or correct your type casting, I strongly
suggest that you use the strong typing option and
use the return value typedefs.
Another point to watch for: with variable
argument prototyping such as that used by GCOOPE,
it is tempting to define methods for the same
generic function that take a different number of
parameters or totally different data types as
arguments. DON'T. That is asking for trouble.
The only exceptions to this are the generic
functions New and Err, the definition of these
functions require that you use different numbers
and types of parameters. Just be careful and
don't carry the practice any further than
absolutely necessary.
TIP: While it is handy to use the same
generic function for many similar methods,
make sure that the methods truly are similar
enough to justify it. Not only are you
tempted to use different parameter types or
numbers as noted above, but you will also be
slowing down your program when you improperly
apply methods to generics. generic functions
with large numbers of methods are slower in
dispatching.
10
2. The Smalltalk Connection
If you have previous experience with
Smalltalk, GCOOPE will smell like home. There are
a few snakes in the garden however. Smalltalk
makes regular use of trivial methods, methods such
as +. While you may define the equivalent Plus
and the like generics in GCOOPE, don't use them
where execution speed is important.
You will have noticed that GCOOPE methods
execute an order of magnitude faster than
Smalltalk methods. This is because GCOOPE methods
are compiled C code while Smalltalk methods are
interpreted code. What may not be so readily
apparent is that GCOOPE's function dispatching is
very similar to Smalltalk's and so it is not much
faster. What happens with 'trivial' methods is
that the execution time for function dispatching
dwarfs the execution time for the method. In
other words, GCOOPE will slow down to near
Smalltalk speeds when executing these types of
methods.
When speed is critical, and you only need to
call such a method once or twice, do these
'trivial' methods locally where possible. For
another idea see the next section.
All in all, the use of Smalltalk techniques
is generally a very good idea, GCOOPE is very
close in concept to the idea of compiled
Smalltalk. They share many of the same strengths
and mannerisms. One trap to avoid is forgetting
that GCOOPE has more capabilities with regards to
inheritance than Smalltalk does, so don't miss out
on reaping those benefits.
3. Local method caching
If you are going to call the same method more
than once or twice in the same statement block or
routine, you can drastically decrease the
execution time of the dispatching process. Just
use the following call:
methodToCall=p(generic, instanceObject);
Where: methodToCall is a method type variable used
to hold the method address; generic is the
generic function index; and instanceObject is
the object type object handle for the
instance.
11
For this technique to apply, instanceObject
must remain constant, or must be a non-inherited
flag class instance of the same constant class.
(flag classes will be explained in detail later,
also see the T.R.M.)
Also if instanceObject is not a non-inherited
flag class, you will have to make the following
call prior to the one above:
instanceObject = steer(ClassOfMethod,
instanceObject);
This will set the instanceObject's instance
tracking variable to correctly point to the
desired instance data.
With all the above limitations, this
technique will not be used often, it can however
be used in some cases to get around the 'trivial'
method penalty described in the previous section.
PROGRAMMER'S NOTE: The best solution to the
need for speed in function dispatching is to
rewrite the dispatcher so that it does not make
any function calls. Then if writing a platform
dependant version, you can drop to assembly and
hand-optimize the result of the first step. This
is the path I used in tests of PCOOPE version 2.1
and while the dispatcher has changed quite a bit,
the new dispatcher should be capable of reaching
comparable speeds. (In 2.1 the speed increase was
quite remarkable (try >10x) and well worth the
effort.) If you have a copy of version 2.1
examine the dispatcher routine for hints.
12
4. Steering the instance
In the section above you caught your first
glimpse of the function steer. It is used to
'steer' the instance tracking mechanism so that
makeInst and getIVptr will work correctly.
Normally this is done automatically by the
dispatcher. However there are certain instances
where you must call steer from within your class
definitions.
a. RULE #2
The rule for the need for a manual call
to steer is: any time you are calling a
generic function that will dispatch to a
method that is defined in an ancestor of the
current class, you must use manual steer
-ing. Prime examples of this situation occur
in methods for the generics New and Kill (and
to a lesser extent in Err). The procedure is
fairly simple:
retVal=g(generic)(steer(ancestor,
instance),[parms]);
Where: ancestor is the class object handle
for the ancestor class; and instance is
the object handle for the current
instance.
This situation occurs because the current
instance is used by the dispatcher to determine
what the current class is. If the current class
and an ancestor class have methods for the same
generic, you could never reach the ancestor method
because the dispatcher would call the current
class method. The above call 'fools' the
dispatcher into believing that the ancestor class
is already the current class and so the call goes
through. Also see the macro ST() in gcoope10.h.
Some commercial products similar in concept
to GCOOPE initialize the ancestor classes for you
prior to calling the child class's method for New.
The problem with this is, that either you end up
with dozens of parameters in the call (to pass all
the ancestor initialization parms and don't ask me
how you track that with multiple ancestory) or you
don't have any control over ancestor
initialization. Frankly, either case is
unacceptable.
13
The above solution isn't what I sought, but
it is usable and achieves the desired purpose.
The only other solutions I could find added way
too much complexity to the kernel code and still
didn't solve the occasional problem of calling an
ancestor method outside of New or Kill. You
would have a bigger, slower function dispatcher,
and still need the steer routine!
You will also need to use the above technique
in your Kill methods wherever they occur, for
exactly the same reasons. Another place where you
may find yourself in need of the above solution is
when you redefine an ancestor method in a way that
adds to the inherited method. In other words,
where you still need to call the ancestor method.
Another way to look at the above is, if you
don't know the ancestor class you don't need to
call steer. Applications programs should never
need it.
Performance wise, the extra call doesn't hurt
you very much. First because the dispatcher won't
have to go through a steer procedure, and second
because it will only occur normally in the
constructor, destructor, or error handler portion
of the class definition. New and Kill methods are
one time cost routines per object instance and if
your code is calling Err very often something is
wrong <GRIN>.
F. Classes and Pseudo Classes
We have not yet reached the point where we are
ready to begin writing class definitions, but in order
to understand some of the next section, you need to
introduce yourself to some of the concepts.
Classes are comprised of an optional class
variable structure definition, an optional instance
variable structure definition, method definitions, and
an installation function.
Pseudo classes are classes that cannot be properly
inherited, they have no class or instance variable
structure definitions, although they may make use of
structure definitions defined internally or externally.
Pseudo classes may define methods, although they don't
have to. They may also define non-generic functions.
They must include an installation procedure.
14
An installation procedure installs the class or
pseudo class definition into the GCOOPE system. It is
responsible for inheritance and genericity. For a
class definition, it is also the place where class
variables may be initialized.
Non-generic functions are simply standard C
library type functions instead of generic function
methods.
Class definitions are where objects are defined,
pseudo class definitions interface to the platform
and/or hardware. Pseudo class definitions are also
used to expand GCOOPE's kernel adding necessary or
optional functionality. The pseudo class concept was
developed because of a desire to treat kernel expansion
in a modular way that could make use of kernel
resources used by normal class definition modules.
G. Flag Classes
The last topic to cover in the GCOOPE environment
is the flag class. A flag class uses a portion of the
object handle as an index to its class object. This
means that the lower 16 bits of the object handle are
no longer needed by the GCOOPE system.
A flag class uses those 16 bits as its instance
variable storage area. Thus, for the overhead of a
simple flag check, flag classes can implement classes
such as Boolean, Char, Int, etc. with no instance
memory being dynamically allocated! For full details
of the object handle structure see the T.R.M.
A flag class is subject to certain limitations.
First, it cannot inherit any class which has instance
variables. Second, it must check to see if it is the
owner of the instance object handle in each method
prior to accessing the instance variable. Third the
New method for such a class must be dual purpose. If
it is being inherited, it must behave exactly as a
normal class.
Basically, the flag class must be careful to check
whether or not it has been inherited with respect to
the current instance. If it has been, it must behave
exactly like a normal class. (Note: a flag class's
ivSize must be given as sufficient to store its
variable type.) Before attempting to create a flag
class definition, it is strongly suggested that you
study the relevant portions of the T.R.M. closely and
also be sure to check the example code provided.
15
III. Creating a class definition
A. Layout of a class definition module
The first statement in a class definition is
#define CLASS <className>. This names the class and
becomes the symbol string name for it. The second
statement is #include "GCOOPE31.h" and the third
statement is object CLASS;.
Following those statements should be your
structure definitions for the class variable and
instance variable structures. These structures are
optional. Hint: instead of creating them as a named
structure, typedef them as a structure.
Next should be the class constructor method
definition, followed by any user defined method
definitions and by a destructor and error handling
method if desired.
The final portion of the class definition is the
class installation function definition.
B. Method definition section
The only method you must define is a constructor.
It should call makeInst and then call the constructors
of each superClass by:
p(New)(steer(superClass,instance),[initParms]);
Other than that, you're on your own as far as
methods go. Just make sure they all fit the following
prototype: retValType methodName(object,...);.
A few points to remember however, if you
dynamically allocate memory within your object:
1. use the kernel memory management shells
s_calloc, s_free, s_malloc, and s_realloc.
2. write a method for Kill that frees the memory
and calls p(Kill)(steer(superClass,
instance)); for each superClass. (immeadiate
parent) and ending with p(Kill)(Object,
instance);.
16
C. Installation routine
The name of the class installation routine MUST be
given as the macro CLASS_INSTALL. This insures
compatibility with the class installation macros. The
first statement after any local variable and pointer
declarations is:
CLASS =
p(New)(Class, sizeof(classVarStruct),
sizeof(instanceVarStruct), [SuperClass object
handle list], END);
This creates the class definition, you may now
optionally get a pointer to the class variables area by
getCVptr and initialize the class variables.
The next step is to add each method, this is done
by:
addPMthd(CLASS, genericName, methodAdr);
You can also block ancestor methods for a generic
via rmvPMthd (see T.R.M.). This is useful for ancestor
methods which should not be directly called except from
a method defined by this class definition.
A note here about inheritance, the call to
p(New)(Class,...) will setup a description of an
instance memory block for instances of this class.
That description includes the instance memory blocks
for all ancestors. After performing that setup, the
class creation method will add ALL the ancestor methods
to the current class automatically. The order that you
specify superClasses (immediate ancestors) is
important. Those given first in the list will take
precedence over later entries. Their methods will
overwrite any conflicting or overlapping methods for a
generic. When you use addPM or rmvPM you will
overwrite or remove any listed ancestor method for the
generic in question.
17
IV. Expansion and Growth
In the short documentation with the PCOOPE version 2.1
release, I foolishly stated that the interface was written
in stone and that all expansion and growth would occur
through the class definitions. Alas, as so aptly put by the
first user's group member, G/PCOOPE isn't ready for that.
He was 100% correct.
In version 1.0 I have attempted to write the kernel in
an expandable manner. That is one reason why the provided
code is optimized for little else than debugging. For each
target platform, an optimized version of the appropriate
kernel module(s) should be created. Low level functions
that prove necessary can be added via the pseudo class
expansion module route. In the design of this system, I
have attempted to provide for the possiblity of future
expansion at each level of the system.
GCOOPE has potential as an application framework. For
that potential to be realized standard, generic, well
defined, and expandable interface specifications need to be
developed. Available public domain libraries that help to
meet these needs should be incorporated as expansion
modules. No doubt, some custom code will have to be
written. The GUI portion alone will prove quite an
undertaking, if GCOOPE is to realize its potential a GUI
specification must provide a path to compatibility with
Windows, X-windows, and other popular GUI environments.
GCOOPE also has potential as an object oriented
operating system extension. In this concept, the GCOOPE
kernel would be a system resource with instantiated memory.
Such an extension would have to include the ability to share
dynamically loaded modules. This type of extension to the
GCOOPE framework is not as difficult as it might sound at
first, GCOOPE could be made available as a process on a Mach
3.0 or QNX microkernel. There would, no doubt, be many
additional demands placed upon the GCOOPE system however.
As of this writing, GCOOPE is in its infancy, at the
very least it serves as an exploratory framework into the
next generation of object oriented programming languages,
application frameworks, and operating systems, at the most
.... you be the judge. Or better yet, join the GCOOPE
user's group and join in the expansion and implementation of
the GCOOPE system.
18
Appendix A. Appendix A. The GCOOPE USER'S GROUP
JOIN THE GCOOPE USER'S GROUP!
For a one year membership send check/money order in the
amount of $20.00, (made payable to Brian Lee Price) to the
address below:
Brian Lee Price
GCOOPE USERS GROUP
RD2 BOX239AA
CLEARVILLE, PA. 15535
Please include my middle name to avoid local post office
difficulties :).
User's group members receive the latest updates to the
GCOOPE system, new class libraries, expansion modules, etc.
Limited free technical support over the phone is available,
and access is granted to a User's group only private limited
access bbs, you will be added to the quarterly newsletter
mailing list, and other services are available.
It is my hope that the user's group members will
participate in the growth of the GCOOPE system, expanding
its libraries and its capabilities. This way, all user's of
the framework will benefit from a degree of co-operation
that is often impossible to achieve with a commercial
product.
19
Appendix B. Appendix B. About the Author (in case you're really
bored)
The author is a starving artist type, currently
unemployed and living in the mountains of south central
Pennsylvania (Hi-technology's equivalent of Bumf---, Egypt).
First introduced to computers after a tour in the USAF as a
television studio technician in 1982. First programming
experience in assembly language on Intertec (who?)
SuperBrain II CP/M and Atari 800XL machines.
Catching the PC wave, the author worked throughout most
of the 80s as a computer technician/consultant ending up
working in the computer evaluation laboratory of Entre'
computer corporate where he was allowed to play with early
networks, CAD/CAM, and the first 386 machines. For the late
80's and early 90's the author was a self employed
electronic systems and circuit designer/consultant having
some success with early video frame digitizer designs for
the Apple Macintosh II before they butchered its expansion
slot capacity.
The last few years spent working with embedded systems
such as traffic control computers and monitors and playing
around with computer languages, the author began his
programming career in C in 1993. GCOOPE is his first
publicly released software package. When not working on the
GCOOPE project or his (t)rusty '70 Ford F-150, the author is
working with local business and political interests to
establish a free public access bbs and Internet link in his
home county.
Anyone need the above talents for a paid job???
20